﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows.Media.Imaging;

namespace STEditor.Classes
{
    public class STGameArea : NotifyObjectBase
    {

        public ObservableCollection<STTile> Tiles { get; private set; }

        public STGame Game { get; set; }

        private int _width;
        public int Width
        {
            get { return _width; }
            set
            {
                _width = value;
                RaisePropertyChanged("Width");
                UpdateTilesCollection();
            }
        }

        private int _height;
        public int Height
        {
            get { return _height; }
            set
            {
                _height = value;
                RaisePropertyChanged("Height");
                UpdateTilesCollection();
            }
        }


        private int _tileWidth;
        public int TileWidth
        {
            get { return _tileWidth; }
            set
            {
                _tileWidth = value;
                RaisePropertyChanged("TileWidth");
            }
        }

        private int _tileHeight;
        public int TileHeight
        {
            get { return _tileHeight; }
            set
            {
                _tileHeight = value;
                RaisePropertyChanged("TileHeight");
            }
        }

        private string _areaName;
        public string AreaName
        {
            get { return _areaName; }
            set
            {
                _areaName = value;
                RaisePropertyChanged("AreaName");
            }
        }

        public STGameArea()
        {
            Tiles = new ObservableCollection<STTile>();
            Tiles.CollectionChanged += TilesChanged;
        }

        private void TilesChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (STTile tile in e.NewItems)
                {
                    tile.GameArea = this;
                }
            }
            if (e.OldItems != null)
            {
                foreach (STTile tile in e.OldItems)
                {
                    tile.GameArea = null;
                }
            }
            RaisePropertyChanged("Tiles");
        }

        private void UpdateTilesCollection()
        {

            if (Width * Height > 0)
            {
                Tiles.CollectionChanged -= TilesChanged;
                Tiles.Clear();
                for (int x = 0; x < Width; x++)
                {
                    for (int y = 0; y < Height; y++)
                    {
                        Tiles.Add(new EmptyTile { Column = x, Row = y, GameArea = this });
                    }
                }
                Tiles.CollectionChanged += TilesChanged;
            }
        }

        public STTile SetTile(int column, int row, STFrame image)
        {


            STTile currentTile = null;
            try
            {
                currentTile = Tiles
                     .Where(t => t.Column == column & t.Row == row)
                     .Single();
            }
            catch (InvalidOperationException)
            {
                throw new ArgumentOutOfRangeException("column, row", "No tile found at column " + column + " and row " + row);
            }

            Tiles.Remove(currentTile);

            var newTile = new STTile { Column = column, Row = row, Image = image, GameArea = this };
            Tiles.Add(newTile);
            return newTile;


        }

        public STTile SetTile(int column, int row)
        {
            if (Game.TileTypes.SelectedTile == null)
            {
                throw new InvalidOperationException("No tile is selected!");
            }

            return SetTile(column, row, Game.TileTypes.SelectedTile);
        }

        public void FillTile(int column, int row)
        {

            // Performs a flood fill 
            List<STTile> filledTiles = new List<STTile>();

            FillTile(column, row, filledTiles);
            filledTiles.ForEach(t => SetTile(t.Column, t.Row));
            
        }

        private void FillTile(int column, int row, List<STTile> filledTiles)
        {

            STTile currentTile = GetTile(column, row);
            if (filledTiles.Contains(currentTile))
            {
                return;
            }

            filledTiles.Add(currentTile);

            STTile left = GetTile(column - 1, row);
            if (left != null && left.Image == currentTile.Image)
            {
                FillTile(column - 1, row, filledTiles);
            }

            STTile right = GetTile(column + 1, row);
            if( right != null && right.Image == currentTile.Image)
            {
                FillTile(column + 1, row, filledTiles);
            }

            STTile up = GetTile(column, row - 1);
            if (up != null && up.Image == currentTile.Image)
            {
                FillTile(column, row - 1, filledTiles);
            }

            STTile down = GetTile(column, row + 1);
            if (down != null && down.Image == currentTile.Image)
            {
                FillTile(column, row + 1, filledTiles);
            }

        }

        public STTile GetTile(int column, int row)
        {
            return
                Tiles
                    .Where(t => t.Column == column && t.Row == row)
                    .FirstOrDefault();
        }

    }
}
